﻿// -----------------------------------------------------------------------
// <copyright file="ModuleFactory.cs" company="Meligo">
// TODO: Update copyright text.
// </copyright>
// -----------------------------------------------------------------------

namespace Meligo.ModuleExtractor.GenerateModuleFactory
{
    using Meligo.ModuleExtractor.GenerateModuleFactory.Entities;
    using Meligo.ModuleExtractor.Helpers;
    using Microsoft.SharePoint;
    using Microsoft.SharePoint.WebPartPages;
    using System;
    using System.Collections.Generic;
    using System.IO;
    using System.Linq;
    using System.Text;
    using System.Web.UI.WebControls;
    using System.Web.UI.WebControls.WebParts;
    using System.Xml;
    using System.Xml.Linq;

    /// <summary>
    /// TODO: Update summary.
    /// </summary>
    /// 
    public class ModuleFactory
    {
        private Dictionary<Guid, ListContext> listContextMap = new Dictionary<Guid, ListContext>();
        private Dictionary<string, PathSiteInfo> pathSiteInfoMap = new Dictionary<string, PathSiteInfo>(StringComparer.OrdinalIgnoreCase);
        private bool useViewTag;
        private bool shortenListViewProperties;
        private bool exportDefaultProperties;
        private string modulePath;
        private string moduleUrl;
        private string filePath;
        private ListItem scopeItem;

        public string GenerateElementsFile(SPFile file, SPList library, bool shortenListViewProperties, bool useViewTag, bool exportDefaultProp, string modulepath, string moduleurl, ListItem scopeitem, string filepath)
        {
            this.shortenListViewProperties = shortenListViewProperties;
            this.useViewTag = useViewTag;
            this.modulePath = modulepath;
            this.moduleUrl = moduleurl;
            this.scopeItem = scopeitem;
            this.filePath = filepath;
            this.exportDefaultProperties = exportDefaultProp;
            XElement moduleElement = this.GetModuleElement();
            moduleElement.Add(this.GetFileElement(file, library));
            return string.Format("<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n<Elements xmlns=\"http://schemas.microsoft.com/sharepoint/\">\r\n{0}\r\n</Elements>", moduleElement.ToString());
        }

        private XElement GetModuleElement()
        {
            return new XElement("Module", new object[] { new XAttribute("Path", this.modulePath), new XAttribute("Url", this.moduleUrl), new XAttribute("Name", "wp" + Guid.NewGuid().ToString().Replace("-", string.Empty).ToUpper()) });
        }

        private XElement GetFileElement(SPFile file, SPList library)
        {
            string name = file.Name;
            string value = (library != null) ? "GhostableInLibrary" : "Ghostable";
            XElement fileElement = null;
            if (!string.IsNullOrEmpty(this.filePath))
            {
                fileElement = new XElement("File", new object[] { new XAttribute("IgnoreIfAlreadyExists", "True"), new XAttribute("Path", this.filePath), new XAttribute("Url", name), new XAttribute("Type", value) });
            }
            else
            {
                fileElement = new XElement("File", new object[] { new XAttribute("IgnoreIfAlreadyExists", "True"), new XAttribute("Path", name), new XAttribute("Url", name), new XAttribute("Type", value) });
            }

            if (library != null && file.Name != null)
            {
                fileElement.Add(this.GetPropertyElements(file.Item));
            }

            if (file.Name.EndsWith(".aspx", StringComparison.OrdinalIgnoreCase))
            {
                fileElement.Add(this.GetWebPartElements(file));
            }

            // Get Connections between WP's 
            fileElement.Add(this.GetConnections(file));

            return fileElement;
        }

        private List<XElement> GetConnections(SPFile file)
        {
            List<XElement> list = new List<XElement>();
            SPWeb parentWeb = file.ParentFolder.ParentWeb;
            SPLimitedWebPartManager limitedWebPartManager = null;
            try
            {
                limitedWebPartManager = parentWeb.GetLimitedWebPartManager(file.Url, PersonalizationScope.Shared);
            }
            catch (Exception ex)
            {
                //TODO: Create logger here
            }

            List<XElement> result;
            if (limitedWebPartManager == null)
            {
                result = list;
            }
            else
            {
                foreach (SPWebPartConnection connection in limitedWebPartManager.SPWebPartConnections)
                {
                    XElement connectionElement = new XElement("WebPartConnection", new object[] { new XAttribute("ID", connection.ID), new XAttribute("ProviderID", connection.ProviderID), new XAttribute("ProviderConnectionPointID", connection.ProviderConnectionPointID), new XAttribute("ConsumerID", connection.ConsumerID), new XAttribute("ConsumerConnectionPointID", connection.ConsumerConnectionPointID), });
                    if (connection.Transformer != null)
                    {
                        // first 2 are being used , but the filtervaluestofiltervaluesTransformer isn't used 
                        if (connection.Transformer is SPRowToParametersTransformer)
                        {
                            SPRowToParametersTransformer rowtransformer = connection.Transformer as SPRowToParametersTransformer;

                            XElement tranformerElement = new XElement("WebPartTransformer", new object[] { new XAttribute("Assembly", "Microsoft.SharePoint,Version=14.0.0.0,Culture=neutral,PublicKeyToken=71e9bce111e9429c"), new XAttribute("Namespace", "Microsoft.SharePoint.WebPartPages"), new XCData("<WebPartPages:SPRowToParametersTransformer  ConsumerFieldNames=\"" + this.GetValues(rowtransformer.ConsumerFieldNames) + "\" ProviderFieldNames=\"" + this.GetValues(rowtransformer.ProviderFieldNames) + "\" />") });
                            connectionElement.Add(tranformerElement);
                        }
                        else if (connection.Transformer is TransformableFilterValuesToParametersTransformer)
                        {
                            TransformableFilterValuesToParametersTransformer transformer = connection.Transformer as TransformableFilterValuesToParametersTransformer;
                            XElement tranformerElement = new XElement("WebPartTransformer", new object[] { new XAttribute("Assembly", "Microsoft.SharePoint,Version=14.0.0.0,Culture=neutral,PublicKeyToken=71e9bce111e9429c"), new XAttribute("Namespace", "Microsoft.SharePoint.WebPartPages"), new XCData("<WebPartPages:TransformableFilterValuesToParametersTransformer  ConsumerFieldNames=\"" + this.GetValues(transformer.ConsumerFieldNames) + "\" ProviderFieldNames=\"" + this.GetValues(transformer.ProviderFieldNames) + "\" />") });
                            connectionElement.Add(tranformerElement);
                        }
                        else if (connection.Transformer is TransformableFilterValuesToFilterValuesTransformer)
                        {
                            TransformableFilterValuesToFilterValuesTransformer transformer = connection.Transformer as TransformableFilterValuesToFilterValuesTransformer;
                            XElement tranformerElement = new XElement("WebPartTransformer", new object[] { new XAttribute("Assembly", "Microsoft.SharePoint,Version=14.0.0.0,Culture=neutral,PublicKeyToken=71e9bce111e9429c"), new XAttribute("Namespace", "Microsoft.SharePoint.WebPartPages"), new XCData("<WebPartPages:TransformableFilterValuesToFilterValuesTransformer MappedConsumerParameterName=\"" + transformer.MappedConsumerParameterName + "\" />") });
                            connectionElement.Add(tranformerElement);
                        }
                    }

                    list.Add(connectionElement);
                }
            }

            if (limitedWebPartManager != null)
            {
                limitedWebPartManager.Web.Dispose();
            }

            //parentWeb.Dispose();
            return list;
        }

        private string GetValues(string[] stringArray)
        {
            StringBuilder builder = new StringBuilder();
            foreach (string stringvalue in stringArray)
            {
                builder.Append(stringvalue);
            }

            return builder.ToString();
        }

        private List<XElement> GetPropertyElements(SPListItem item)
        {
            List<XElement> list = new List<XElement>();
            ListContext context = this.GetListContext(item.ParentList);
            string fieldValue = string.Empty;
            if (item.Fields.ContainsField("Content Type"))
            {
                fieldValue = item.Fields["Content Type"].ToString();
            }

            List<XElement> result;
            if (string.IsNullOrEmpty(fieldValue) || !context.ContentTypeMap.ContainsKey(fieldValue))
            {
                result = list;
            }
            else
            {
                HashSet<string> hashSet = new HashSet<string>();
                list.Add(Helper.PropertyNodeGetter.GetPropertyNode("Content Type", fieldValue));
                hashSet.Add("Content Type");
                if (context.FieldNameMap.ContainsKey("PublishingPageLayout"))
                {
                    SPField publishingPageLayoutField = context.FieldNameMap["PublishingPageLayout"];
                    list.Add(Helper.PropertyNodeGetter.GetPropertyNode("PublishingPageLayout", Helper.FieldToValueGetter.FieldValueToString(publishingPageLayoutField, item.Fields[publishingPageLayoutField.Id], this.pathSiteInfoMap)));
                    list.Add(Helper.PropertyNodeGetter.GetPropertyNode("PublishingPageLayout", Helper.FieldToValueGetter.FieldValueToString(publishingPageLayoutField, item.Fields[publishingPageLayoutField.Id], this.pathSiteInfoMap)));
                    hashSet.Add("PublishingPageLayout");
                }

                foreach (SPField current in from fid in context.ContentTypeMap[fieldValue] where context.FieldMap.ContainsKey(fid) select context.FieldMap[fid])
                {
                    if (!hashSet.Contains(current.InternalName))
                    {
                        list.Add(Helper.PropertyNodeGetter.GetPropertyNode(current.InternalName, Helper.FieldToValueGetter.FieldValueToString(current, item.Fields[current.Id], this.pathSiteInfoMap)));
                        hashSet.Add(current.InternalName);
                    }
                }

                result = list;
            }

            return result;
        }

        private ListContext GetListContext(SPList list)
        {
            if (!this.listContextMap.ContainsKey(list.ID))
            {
                this.listContextMap[list.ID] = new ListContext(list);
            }

            return this.listContextMap[list.ID];
        }

        [SPDisposeCheckIgnore(SPDisposeCheckID.SPDisposeCheckID_160, "Disposing is done properly")]
        private List<XElement> GetWebPartElements(SPFile file)
        {
            List<XElement> list = new List<XElement>();
            List<XElement> result = null;

            SPWeb parentWeb = file.ParentFolder.ParentWeb;
            SPLimitedWebPartManager limitedWebPartManager = null;
            try
            {
                limitedWebPartManager = parentWeb.GetLimitedWebPartManager(file.Url, PersonalizationScope.Shared);
            }
            catch (Exception ex)
            {
                if (limitedWebPartManager != null)
                {
                    limitedWebPartManager.Web.Dispose();
                }

                //TODO: Logger.WriteEntry("GetWebPartElements: ", "an error has occured", ex, parentWeb.Site.Url);
            }

            try
            {
                if (limitedWebPartManager == null)
                {
                    result = list;
                }
                else
                {
                    foreach (System.Web.UI.WebControls.WebParts.WebPart webPart in limitedWebPartManager.WebParts)
                    {
                        if (!(webPart.GetType().Name == "ErrorWebPart"))
                        {
                            string zoneID = limitedWebPartManager.GetZoneID(webPart);
                            int num = webPart.ZoneIndex + 1;
                            string exportString = this.GetWPExportString(webPart, limitedWebPartManager);
                            XElement viewElement = new XElement("Dummy");
                            if (webPart is XsltListViewWebPart)
                            {
                                if (this.useViewTag)
                                {
                                    XsltListViewWebPart listViewWebPart = webPart as XsltListViewWebPart;
                                    SPList sourceList = parentWeb.Lists[new Guid(listViewWebPart.ListName)];
                                    string dump = sourceList.GetUncustomizedViewByBaseViewId(0).HtmlSchemaXml;
                                    SPView listView = sourceList.Views[new Guid(listViewWebPart.ViewGuid)];
                                    if (this.scopeItem != null)
                                    {
                                        viewElement = new XElement("View", new object[] { new XAttribute("WebPartZoneID", zoneID), new XAttribute("WebPartOrder", num), new XAttribute("List", sourceList.RootFolder.Url.Trim(new char[] { '/' })), new XAttribute("BaseViewID", listView.BaseViewID), new XAttribute("Scope", this.scopeItem.Value), new XAttribute("Type", listView.Type), new XAttribute("ID", listViewWebPart.ID), new XCData(Constants.GetListViewWebpart(exportString, sourceList.Title)) });
                                    }
                                    else
                                    {
                                        viewElement = new XElement("View", new object[] { new XAttribute("WebPartZoneID", zoneID), new XAttribute("WebPartOrder", num), new XAttribute("List", sourceList.RootFolder.Url.Trim(new char[] { '/' })), new XAttribute("BaseViewID", listView.BaseViewID), new XAttribute("Scope", listView.Scope.ToString()), new XAttribute("Type", listView.Type), new XAttribute("ID", listViewWebPart.ID), new XCData(Constants.GetListViewWebpart(exportString, sourceList.Title)) });
                                    }
                                }
                                else
                                {
                                    if (!this.shortenListViewProperties && this.exportDefaultProperties)
                                    {
                                        viewElement = new XElement("AllUsersWebPart", new object[] { new XAttribute("ID", webPart.ID), new XAttribute("WebPartZoneID", zoneID), new XAttribute("WebPartOrder", num), new XCData(exportString) });
                                    }
                                    else
                                    {
                                        XNamespace ns = "http://schemas.microsoft.com/WebPart/v3";
                                        XDocument exportStringDoc = XDocument.Parse(exportString, LoadOptions.None);
                                        IEnumerable<XElement> lijst = from node in exportStringDoc.Root.Descendants(ns + "property")
                                                                      select node;
                                        StringBuilder builder = new StringBuilder();
                                        List<XElement> shortPropertiesList = new List<XElement>();
                                        foreach (XElement elements in lijst)
                                        {
                                            switch (elements.Attribute("name").Value)
                                            {
                                                case "Title":
                                                    shortPropertiesList.Add(elements);
                                                    break;
                                                case "TitleUrl":
                                                    XElement tempElement = elements;
                                                    tempElement.SetAttributeValue("name", "ListUrl");
                                                    shortPropertiesList.Add(elements);
                                                    break;
                                                case "Description":
                                                    shortPropertiesList.Add(elements);
                                                    break;
                                                case "Hidden":
                                                    shortPropertiesList.Add(elements);
                                                    break;
                                                case "XmlDefinition":
                                                    shortPropertiesList.Add(elements);
                                                    string greaterthan = "&lt;";
                                                    string smallerthan = "&gt;";

                                                    string valuexml = elements.Value;
                                                    valuexml = valuexml.Replace(greaterthan, "<");
                                                    valuexml = valuexml.Replace(smallerthan, ">");

                                                    XDocument tempDoc = XDocument.Parse(valuexml);
                                                    XElement rootElement = tempDoc.Element("View");
                                                    string baseviewID = rootElement.Attribute("BaseViewID").Value;

                                                    XElement element = new XElement(ns + "property", new XAttribute("name", "BaseViewID"));
                                                    element.Value = baseviewID;
                                                    shortPropertiesList.Add(element);
                                                    break;
                                                default:
                                                    break;
                                            }

                                            builder.Append(elements.ToString());
                                        }

                                        XElement propertiesToRemove = exportStringDoc.Root.Element(ns + "webPart").Element(ns + "data").Element(ns + "properties");
                                        propertiesToRemove.Remove();
                                        XElement propertiesNode = new XElement(ns + "properties", new object[] { shortPropertiesList });
                                        exportStringDoc.Root.Element(ns + "webPart").Element(ns + "data").Add(propertiesNode);

                                        viewElement = new XElement("AllUsersWebPart", new object[] { new XAttribute("ID", webPart.ID), new XAttribute("WebPartZoneID", zoneID), new XAttribute("WebPartOrder", num), new XCData(exportStringDoc.ToString()) });
                                    }
                                }
                            }
                            else
                            {
                                viewElement = new XElement("AllUsersWebPart", new object[] { new XAttribute("ID", webPart.ID), new XAttribute("WebPartZoneID", zoneID), new XAttribute("WebPartOrder", num), new XCData(exportString) });
                            }

                            if (viewElement != null)
                            {
                                if (viewElement.Name != "Dummy")
                                {
                                    list.Add(viewElement);
                                }
                            }
                        }
                    }

                    if (limitedWebPartManager != null)
                    {
                        limitedWebPartManager.Web.Dispose();
                    }

                    result = list;
                }
            }
            catch (Exception ex)
            {
                //TODO: Logger.WriteEntry("ModuleFactory", "an error has occured", ex, parentWeb.Site.Url);
            }
            finally
            {
                if (limitedWebPartManager != null)
                {
                    limitedWebPartManager.Web.Dispose();
                }

                //parentWeb.Dispose();
            }

            return result;
        }

        private string GetPropertyNodeValue(string exportString, string propertyname, string attributename, bool inXmldefinition, bool inParameterBindings)
        {
            string nodevalue = string.Empty;

            XNamespace ns = "http://schemas.microsoft.com/WebPart/v3";
            XDocument exportStringDoc = XDocument.Parse(exportString, LoadOptions.None);
            IEnumerable<XElement> lijst = null;
            if (!inXmldefinition && !inParameterBindings)
            {
                lijst = from node in exportStringDoc.Root.Descendants(ns + "property") select node;
            }
            else if (inXmldefinition && !inParameterBindings)
            {
                lijst = from node in exportStringDoc.Root.Descendants(ns + "property") where node.FirstAttribute.Value.ToLower() == "xmldefinition" select node;
            }
            else if (!inXmldefinition && inParameterBindings)
            {
                lijst = from node in exportStringDoc.Root.Descendants(ns + "property") where node.FirstAttribute.Value.ToLower() == "parameterbindings" select node;
            }

            if (lijst != null)
            {
                foreach (XElement elements in lijst)
                {
                    if (!inXmldefinition)
                    {
                        // getting the value of a node
                        if (!string.IsNullOrEmpty(propertyname))
                        {
                            if (elements.Attribute("name").Value == propertyname)
                            {
                                nodevalue = elements.Value;
                            }
                        }
                    }
                    else if (inXmldefinition && !inParameterBindings)
                    {
                        if (!string.IsNullOrEmpty(attributename))
                        {
                            // getting the value in the xmldefinition node from the view tag, NOT from the nodes below the view tag
                            string greaterthan = "&lt;";
                            string smallerthan = "&gt;";

                            string valuexml = elements.Value;
                            valuexml = valuexml.Replace(greaterthan, "<");
                            valuexml = valuexml.Replace(smallerthan, ">");

                            XDocument tempDoc = XDocument.Parse(valuexml);
                            XElement rootElement = tempDoc.Element("View");
                            nodevalue = rootElement.Attribute(attributename).Value;
                        }
                    }
                    else if (!inXmldefinition && inParameterBindings)
                    {
                        // TODO: add logic here
                    }
                }
            }

            return nodevalue;
        }

        private string GetWPExportString(System.Web.UI.WebControls.WebParts.WebPart part, SPLimitedWebPartManager manager)
        {
            StringBuilder stringBuilder = new StringBuilder();
            using (StringWriter stringWriter = new StringWriter(stringBuilder))
            {
                part.ExportMode = WebPartExportMode.All;
                manager.ExportWebPart(part, new XmlTextWriter(stringWriter) { Formatting = Formatting.Indented });
            }

            string text = stringBuilder.ToString();
            if (text.IndexOf("<![CDATA[") > 0)
            {
                XElement webpartElement = XElement.Parse(text);
                XNode[] array = (
                    from n in webpartElement.DescendantNodes()
                    where n is XCData
                    select n).ToArray<XNode>();
                for (int i = 0; i < array.Length; i++)
                {
                    XCData data = (XCData)array[i];
                    data.ReplaceWith(new XText(data.Value));
                }

                text = webpartElement.ToString();
            }

            if (part is ListViewWebPart)
            {
                XElement listViewElement = XElement.Parse(text);
                XElement listViewWebPartElement = (
                    from e in listViewElement.Elements()
                    where e.Name.LocalName == "ListViewXml"
                    select e).FirstOrDefault<XElement>();
                if (listViewWebPartElement != null)
                {
                    listViewWebPartElement.Remove();
                }

                text = listViewElement.ToString();
            }

            return text;
        }
    }
}
